package auth

import (
	"encoding/json"
	"errors"
	"gitee.com/baizhige/simple-auth-go/schema"
	"gitee.com/baizhige/simple-auth-go/util"
)

var ErrTokenNotFInd = errors.New("can not find token in request") // 没有找到用户
var ErrApiInfoNotFInd = errors.New("can not find api by apiMap")  //没有找到接口信息
var ErrApiAuthFail = errors.New("UNAUTHORIZED")                   //无权限

// Login 记录用户登录状态
func Login(c schema.Response, user schema.User) (token string, err error) {
	token = util.CryptoRand(32)
	config := user.GetLoginConfig()

	err = cache.Set(key(config.TokenKey, token), user, config.Expire)
	if err != nil {
		return
	}

	c.Header(config.TokenKey, token)
	return
}

// Logout 清理用户缓存
func Logout[T schema.User](c schema.Request) (err error) {
	var user T
	config := user.GetLoginConfig()
	token := c.GetHeader(config.TokenKey)
	if token == "" {
		return ErrTokenNotFInd
	}
	err = cache.Del(key(config.TokenKey, token))
	return
}

// GetAuthUser 获取本次请求对应的用户
func GetAuthUser[T schema.User](c schema.Request) (user T, err error) {
	var info T
	config := info.GetLoginConfig()
	// 获取token
	token := c.GetHeader(config.TokenKey)
	if token == "" {
		err = ErrTokenNotFInd
		return
	}

	// 获取用户
	redisKey := key(config.TokenKey, token)
	value, err := cache.Get(redisKey)
	if err != nil {
		return
	}

	// 解析用户
	err = json.Unmarshal([]byte(value), &info)
	if err != nil {
		return
	}
	return info, nil
}

// LoginRefresh 登录状态刷新，保持登录状态
func LoginRefresh[T schema.User](c schema.Request) (err error) {
	var user T
	config := user.GetLoginConfig()
	// 获取token
	token := c.GetHeader(config.TokenKey)
	if token == "" {
		err = ErrTokenNotFInd
		return
	}
	// 续期
	err = cache.Expire(key(config.TokenKey, token), config.Expire)
	return
}

// Authentication 验证用户是否有权限，可以在中间件里调用
func Authentication[T schema.User](method string, c schema.Request) (err error) {
	//取得接口信息
	apiInfo, ok := apiMap[apiKey(method, c.FullPath())]
	if !ok {
		return ErrApiInfoNotFInd
	}
	//无需鉴权则放行
	if !apiInfo.Auth {
		return
	}
	//取得上下文用户
	user, err := GetAuthUser[T](c)
	if err != nil {
		return
	}
	authorizations := user.GetAuthorizations()
	if !util.ArrExists(authorizations, apiInfo.Code) {
		err = ErrApiAuthFail
	}
	return
}

// 组装唯一key
func key(tokenKey, token string) string {
	return "token:" + tokenKey + ":" + token
}
